home *** CD-ROM | disk | FTP | other *** search
- '
- ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
- ' Playback-Version des Programms, zur Wiedergabe von MIDI-Standard-Dateien
- ' (ohne Taktanzeige usf. - dafür deutlich besseres Timing)
- ' geschrieben in GFA-BASIC 3.5E D, (C)1993 Stephan M. Sprenger
- ' PROSONIQ PRODUCTS SOFTWARE, Badenwerkstraße 9, 76137 Karlsruhe
- ' .............................................................................
- '
- '
- $I+ ! Compiler-Option: Interrupts initialisieren
- $P> ! Compiler-Option: GFA-Konvention für Procedures
- '
- mtc!=FALSE ! Synchronisation: FALSE=intern, TRUE=MIDI Clock
- tempo#=0.01 ! Tempo: Aufzeichnungs- und Abspielgeschwindigkeit
- '
- @init ! Programm initialisieren
- @standby ! Hauptschleife des Programms
- '
- ' Unter-Routinen zum Programm
- '
- PROCEDURE init
- OPENW 0 ! Bildschirm und Fenster...
- DEFMOUSE 0
- TITLEW #1," SEQUENCER - Playback-Only Version "
- INFOW #1," "
- OPENW #1,20,150,600,100,0
- CLS
- dm#=FRE(0)/15
- DIM dat$(dm#) ! Feld für MIDI-Daten
- DIM time%(dm#) ! Feld für Absolutzeit
- DIM d_len&(300)
- OPEN "O",#99,"MID:" ! MIDI-Schnittstelle öffnen
- RETURN
- '
- PROCEDURE standby
- DO
- INFOW #1," Status: Stand-By "+fn$
- PRINT AT(28,2);"NO BAR COUNT DISPLAY"
- key$=CHR$(INP(2))
- IF key$="0"
- INFOW #1," Status: Playback ... "+fn$
- @playback
- ELSE IF key$=CHR$(27)
- @ende
- ELSE IF key$="l" OR key$="L"
- INFOW #1," Status: Loading MIDI Standard File ... "+fn$
- @convert_mid(1,0)
- ELSE IF INKEY$=CHR$(13)
- @all_notes_off
- ELSE
- CLR key$
- ENDIF
- LOOP
- RETURN
- '
- PROCEDURE playback
- tm#=0
- c%=1
- VOID FRE(0)
- dummy$=INPMID$ ! MIDI-In-Puffer leeren ...
- HIDEM
- DO ! Wiedergabe-Schleife
- IF INP?(3)
- in|=INP(3)
- ELSE
- in|=0
- ENDIF
- IF in|=248 AND mtc!=TRUE ! MIDI-Clock?
- INC tm# ! Dann Uhr +1
- ENDIF
- IF mtc!=FALSE ! Interne Synchronisation?
- DELAY tempo# ! Dann Uhr nach Pause +1
- INC tm#
- ENDIF
- IF time%(c%)<=tm# ! nächstes Event schon dran?
- PRINT #99,dat$(c%); ! Ja, dann ausgeben
- INC c%
- ENDIF
- EXIT IF c%=>dm#-1 OR c%=>last% OR INKEY$=CHR$(13)
- LOOP
- SHOWM
- @all_notes_off ! Nach Beendigung alle Noten aus.
- RETURN
- '
- PROCEDURE ende
- CLOSEW 1
- CLOSEW 0
- CLOSE
- EDIT
- RETURN
- '
- PROCEDURE convert_mid(abs_rel|,save_load|)
- '
- ' speichert/lädt Daten als MIDI Standard File:
- ' MIDI-Events in dat$(), Zeit als Clicks in time%()
- '
- ' Wenn Sie abs_rel| auf 1 setzen, erwartet die Routine in time%() die
- ' Zeitangabe als Absolutzeit (also als Zeit seit Spuranfang); ist
- ' abs_rel| Null, dann erwartet die Routine in time%() die seit dem
- ' vorhergehenden Event verstrichene Zeit.
- '
- ' In dat$() befinden sich die zu speichernden MIDI-Events; diese müssen
- ' pro Eintrag vollständig sein (ein Note-On liegt hier z.B. als
- ' dat$(2)=CHR$(144)+CHR$(60)+CHR$(64) vor).
- '
- ' Wenn save_load|=1 ist wird gespeichert, bei save_load|=0 wird geladen
- '
- @convert_init ! Konvertierungsroutine einrichten
- dummy$=INKEY$
- FILESELECT "A:\*.MID","MIDIFILE.MID",fn$
- DEFMOUSE 2
- IF fn$="" OR fn$="\"
- GOTO cancel
- ENDIF
- IF save_load|=1
- OPEN "O",#1,fn$
- PRINT #1,"MThd"; ! **** Header-Block-Kennung ****
- PRINT #1,MKL$(6); ! Länge des Headers
- PRINT #1,MKI$(0); ! File Format (0)
- PRINT #1,MKI$(1); ! Anzahl der Spuren
- PRINT #1,MKI$(&H18); ! 24 Clicks pro Viertelnote (MIDI Clock)
- PRINT #1,"MTrk"; ! ***** Spur-Block-Kennung *****
- PRINT #1,MKL$(0); ! Platzhalter für Blocklänge
- PRINT #1,CHR$(0); ! Delta-Time des ersten Events
- PRINT #1,CHR$(&HFF); ! Meta-Event: Spurname
- PRINT #1,MKI$(&H108); ! (01=Text, 08=Textlänge)
- PRINT #1,"MIDIFILE"; ! Spurname
- c%=0
- DO
- INC c%
- VOID FRE(0)
- r%=V:r$
- IF abs_rel|=1 ! time%() enthält Absolutzeit
- LPOKE r%+6,(time%(c%)-time%(c%-1)) ! Zeit zwischen zwei Events
- ELSE
- LPOKE r%+6,time%(c%) ! time%() enthält Delta-Time
- ENDIF
- POKE r%+2,1 ! Opcode: 1=DEZ-->VLN, 0=VLN-->DEZ
- d0#=C:r%() ! VLN-Routine aufrufen
- FOR b|=0 TO 3 ! Bytes in Datei schreiben...
- IF PEEK(r%+6+b|)<>0
- PRINT #1,CHR$(PEEK(r%+6+b|));
- ENDIF
- NEXT b|
- PRINT #1,dat$(c%); ! MIDI-Daten in Datei ausgeben
- EXIT IF c%=last% ! Abbrechen wenn Spurende
- LOOP
- PRINT #1,CHR$(0); ! Delta-time% zum Spurende
- PRINT #1,CHR$(&HFF);MKI$(&H2F00); ! Meta-Event: Spurende
- CLOSE #1
- OPEN "U",#1,fn$ ! Datei nochmals öffnen
- l%=LOF(#1) ! Länge ermitteln
- SEEK #1,18 ! Byte #18 anfahren
- PRINT #1,MKL$(l%-18); ! Spurlänge eintragen
- ELSE
- c%=0 ! Zähler für Feldindex
- z%=0 ! Zähler für Absolutzeit
- b%=0 ! Zähler für gelesene Bytes
- evt$=""
- OPEN "I",#1,fn$ ! Datei zum Lesen öffnen
- id$=INPUT$(4,#1) ! Header-ID lesen
- h_l%=CVL(INPUT$(4,#1)) ! Header-Länge lesen
- ff&=CVI(INPUT$(2,#1)) ! File-Typ lesen
- anz&=CVI(INPUT$(2,#1)) ! Spuranzahl
- res&=CVI(INPUT$(2,#1)) ! Auflösung
- IF id$<>"MThd" OR ff%<>0 ! MIDI-File vom Typ 0?
- ALERT 3,"Ungültiges Fileformat - |Fileheader",1," OK ",r#
- GOTO cancel ! Nein, dann Abbruch
- ENDIF
- id$=INPUT$(4,#1) ! Track-ID lesen
- b_l%=CVL(INPUT$(4,#1)) ! Länge der Spur
- IF id$<>"MTrk" ! Track-ID prüfen
- ALERT 3,"Ungültiges Fileformat - |Trackstruktur",1," OK ",r#
- GOTO cancel ! Ungültig? dann raus
- ENDIF
- DO ! Leseschleife
- INC c% ! Zähler für Feld erhöhen
- t$=""
- DO ! Leseschleife für Zeit
- tim$=INPUT$(1,#1) ! Erstes Byte lesen
- INC b% ! Bytezähler +1
- t$=t$+tim$ ! ersten Zeitwert bilden
- EXIT IF ASC(tim$)<128 ! raus, wenn LSB gelesen
- LOOP
- VOID FRE(0)
- t$=MID$(t$,1,4)
- t$=STRING$(4-LEN(t$),0)+t$ ! Langwort bilden
- r%=V:r$ ! Adresse der Maschinenroutine
- POKE r%+2,0 ! Opcode: 0=VLN-->DEZ
- LPOKE r%+6,CVL(t$) ! Wert übergeben
- d0#=C:r%() ! Maschinenprogramm aufrufen
- IF abs_rel|=0 ! Relative Zeit benötigt?
- time%(c%)=LPEEK(r%+6)/(res&/24) ! dann Delta-Time ablegen
- ELSE ! Absolutzeit?
- z%=z%+LPEEK(r%+6) ! dann Zeit bilden...
- time%(c%)=z%/(res&/24) ! ...und in interne Auflösung umrechnen
- ENDIF
- evt$=INPUT$(1,#1) ! Event, erstes Byte lesen
- INC b% ! wieder ein Byte mehr...
- rb|=(ASC(evt$)) AND &HF0 ! nur Hi-Nibble wird benötigt
- IF d_len&(rb|)=0 ! Event-Typ ist nicht bekannt?
- ALERT 3,"ERROR:|Unbekanntes MIDI-Event",1,"CANCEL",r#
- EXIT IF r#=1 ! dann nix wie raus...
- ENDIF
- IF evt$=CHR$(&HFF) ! Event ist Meta-Event...
- m_type&=ASC(INPUT$(1,#1)) ! ...dann Event-Typ feststellen
- m_l&=ASC(INPUT$(1,#1)) ! Länge feststellen
- RELSEEK #1,m_l& ! Meta-Event ignorieren...
- b%=b%+m_l&+2 ! ...und überspringen
- EXIT IF m_type&=&H2F ! raus wenn Meta-Event = Spurende
- ELSE
- dat$(c%)=evt$+INPUT$(d_len&(rb|)-1,#1) ! wenn normales MIDI-Event...
- b%=b%+d_len&(rb|) ! ... dann lesen und speichern
- ENDIF
- LOOP
- last%=c% ! Spurende
- ENDIF
- cancel:
- CLOSE #1 ! Datei schließen
- DEFMOUSE 0 ! Biene wegzaubern
- RETURN
- '
- PROCEDURE convert_init
- '
- ' Initialisierung der VLN/Dezimal-Routine.
- ' Die in r$ abgelegte Maschinenroutine erwartet in V:r$+2 als Byte den
- ' Opcode der auszuführenden Berechnung (0=VLN in Dezimalwert umrechnen,
- ' 1=Dezimalwert in VLN umrechnen) und in V:r$+6 den umzurechnenden Wert.
- ' Nach Aufruf der Routine kann von dort auch der berechnete Wert gelesen
- ' werden (Langwort). Die Wertübergabe über die Speicherstelle habe ich
- ' gewählt, weil die Übergabe über den Stack bei meiner GFA-Version nicht
- ' korrekt funktioniert.
- '
- r$=MKL$(&H60080000)+MKL$(0)+MKL$(&H41FA)+MKL$(&HFFF62228)
- r$=r$+MKL$(&H44A10)+MKL$(&H6754B2BC)+MKL$(&HFFFFFFF)+MKL$(&H62407003)
- r$=r$+MKL$(&HE389E209)+MKL$(&H8810007)+MKL$(&HB07C0003)+MKL$(&H67084A01)
- r$=r$+MKL$(&H670408C1)+MKL$(&H71401)+MKL$(&HE09AE089)+MKL$(&H51C8FFE2)
- r$=r$+MKL$(&H8020017)+MKL$(&H670408C2)+MKL$(&HF0802)+MKL$(&H1F6706)
- r$=r$+MKL$(&H820080)+MKL$(&H80002142)+MKL$(&H47000)+MKL$(&H4E75217C)
- r$=r$+MKL$(&HFFFFFFF7)+MKL$(&H470FF)+MKL$(&H4E75B2BC)+MKL$(&HFFFFFFF7)
- r$=r$+MKL$(&H62167003)+MKL$(&H1401EE9A)+MKL$(&HE08951C8)+MKL$(&HFFF8E89A)
- r$=r$+MKL$(&H2820FFF)+MKL$(&HFFFF60CE)+MKL$(&H217C0FFF)+MKL$(&HFFFF0004)
- r$=r$+MKL$(&H70FF4E75)
- '
- '
- d_len&(128)=3 ! Note-Off,Befehlslänge 3 Bytes
- d_len&(144)=3 ! Note-On, Befehlslänge 3 Bytes
- d_len&(160)=3 ! Poly Pressure
- d_len&(176)=3 ! Control Change
- d_len&(192)=2 ! Program Change
- d_len&(208)=2 ! Channel Pressure
- d_len&(224)=3 ! Pitch Wheel Change
- d_len&(&HF0)=&HFF ! Meta-Event darf keinen Fehler erzeugen
- '
- ' Es können hier weitere Befehlslängen für MIDI-Nachrichten eingetragen werden
- '
- RETURN
- '
- PROCEDURE all_notes_off
- FOR chan|=0 TO 15 ! Alle Kanäle durchgehen...
- FOR note|=0 TO 127 ! Alle Noten durchgehen...
- IF note|=0 ! Erstes Note-Off normal..
- OUT 3,128+chan|
- ENDIF
- OUT 3,note|,0 ! ..den Rest im Running-Mode
- NEXT note| ! senden (geht schneller)
- NEXT chan|
- RETURN
-